package com.pig4cloud.pigx.contract.executer.trade.handler.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.customize.contants.BigDecimalConstants;
import com.pig4cloud.pigx.common.customize.contants.MongoTableNameConstants;
import com.pig4cloud.pigx.common.customize.enums.CommonDictEnum;
import com.pig4cloud.pigx.common.customize.exception.MqAckException;
import com.pig4cloud.pigx.common.customize.mysql.utils.MysqlUtil;
import com.pig4cloud.pigx.common.customize.security.utils.SecurityUtil;
import com.pig4cloud.pigx.common.customize.utils.CommonUtil;
import com.pig4cloud.pigx.common.seata.utils.SeataUtils;
import com.pig4cloud.pigx.contract.bo.ContractTradeHandleBO;
import com.pig4cloud.pigx.contract.bo.ContractTradeNewBO;
import com.pig4cloud.pigx.contract.dto.ContractPositionMarginAppendDTO;
import com.pig4cloud.pigx.contract.entity.ContractOrder;
import com.pig4cloud.pigx.contract.entity.ContractPosition;
import com.pig4cloud.pigx.contract.enums.ContractOrderEnum;
import com.pig4cloud.pigx.contract.enums.ContractPositionEnum;
import com.pig4cloud.pigx.contract.executer.order.supporter.OrderCalculator;
import com.pig4cloud.pigx.contract.executer.position.supporter.PositionCalculator;
import com.pig4cloud.pigx.contract.executer.trade.handler.TradeHandler;
import com.pig4cloud.pigx.contract.mapper.ContractOrderMapper;
import com.pig4cloud.pigx.contract.mapper.ContractPositionMapper;
import com.pig4cloud.pigx.contract.mapper.ContractTradeMapper;
import com.pig4cloud.pigx.contract.mapstruct.ContractTradeStruct;
import com.pig4cloud.pigx.contract.mongo.entity.ContractOrderMongo;
import com.pig4cloud.pigx.contract.mongo.entity.ContractSymbolMongo;
import com.pig4cloud.pigx.contract.mongo.entity.ContractTradeMongo;
import com.pig4cloud.pigx.contract.utils.ContractUtils;
import com.pig4cloud.pigx.wallet.dto.WalletContractAccountChangeDTO;
import com.pig4cloud.pigx.wallet.enums.WalletContractAccountDetailEnum;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 成交记录处理器抽象实现
 *
 * @since 2022/8/17 11:17
 */
@Slf4j
@AllArgsConstructor
public abstract class AbstractTradeHandler implements TradeHandler {

    protected final ContractOrderMapper contractOrderMapper;
    protected final ContractTradeMapper contractTradeMapper;
    protected final ContractPositionMapper contractPositionMapper;

    protected final RedisTemplate redis;
    protected final MongoTemplate mongo;

    protected final CommonUtil commonUtil;
    protected final MysqlUtil mysqlUtil;
    protected final SecurityUtil securityUtil;
    protected final ContractUtils contractUtils;
    protected final PositionCalculator positionCalculator;

    /**
     * 获取成交记录
     *
     * @param tradeId tradeId
     * @return {@link ContractTradeMongo}
     * @since 2022/8/17 21:21
     */
    @Override
    public ContractTradeMongo getTrade(Long tradeId) {
        if (Objects.isNull(tradeId)) {
            throw new MqAckException("数据异常，tradeId为空");
        }

        // 从mongodb中查询
        Criteria criteria = Criteria.where("id").is(tradeId);
        ContractTradeMongo trade = mongo.findOne(Query.query(criteria),
                ContractTradeMongo.class,
                MongoTableNameConstants.CONTRACT_TRADE_NEW);

        if (Objects.isNull(trade)) {
            throw new MqAckException("数据异常，未能获取到交易记录信息");
        }

        return trade;
    }

    /**
     * 成交记录处理
     *
     * @param bo bo
     * @since 2022/8/17 11:15
     */
    @Override
    public void handle(ContractTradeHandleBO bo) {
        // log.info("执行的Handler：" + this.getClass().getSimpleName());

        ContractTradeMongo trade = bo.getMongo();
        List<ContractTradeNewBO> list = bo.getNewBoList();
        // 转换为BO
        ContractTradeNewBO taker = ContractTradeStruct.INSTANCE.mongo2TradeNewBO(trade);
        list.add(taker);

        // 查询对手成交记录，转换为BO
        ContractTradeMongo makerTrade = getTrade(trade.getMakerTradeId());
        ContractTradeNewBO maker = ContractTradeStruct.INSTANCE.mongo2TradeNewBO(makerTrade);
        taker.setMakerBo(maker);

		/*
		 如果是机器人吃单，taker是用户，maker是机器人。
		 机器人订单不参与处理,只持久化成交记录
		 */
        if (Objects.equals(CommonDictEnum.YesOrNo.YES.getValue(), maker.getIsRobot())) {
            maker.setPositionId(0L);
            saveToMysql(maker);
            mongo.remove(Query.query(Criteria.where("_id").is(maker.getId())), ContractTradeMongo.class);
        } else {
            list.add(maker);
        }

        // 开仓尝试新增仓位，并预查询仓位id
        initPositionWhenOpen(list);

        // 开启本地事务
        mysqlUtil.startLocalTx();
        // 根据id查询仓位，并锁定
        lockPositionWhenTrade(list);
        // 根据id查询订单，并锁定
        lockOrderWhenTrade(list);

        // 遍历，分别处理
        list.forEach(i -> {
            // 初始化信息
            initParam(i);
            // 计算费用
            calculateFee(i);
            // 持久化成交记录
            saveToMysql(i);
        });

        // 处理订单
        handleOrder(list);
        // 处理仓位
        handlePosition(list);
    }

    /**
     * 根据id查询订单，并锁定
     *
     * @param list list
     * @since 2022/10/14 14:30
     */
    private void lockOrderWhenTrade(List<ContractTradeNewBO> list) {
        // 收集订单id
        List<Long> orderIdList = list.stream()
                .map(ContractTradeNewBO::getOrderId)
                .collect(Collectors.toList());

        // 查询双方订单，并锁定
        QueryWrapper<ContractOrder> query = new QueryWrapper<>();
        query.lambda()
                .in(ContractOrder::getId, orderIdList)
                .in(ContractOrder::getStatus, ContractOrderEnum.Status.ON_GOING.getValue(),
                        ContractOrderEnum.Status.PARTIAL_DEAL.getValue())
                .last("for update");
        List<ContractOrder> orderList = contractOrderMapper.selectList(query);
        if (orderList.size() != list.size()) {
            throw new MqAckException("未能获取到订单信息");
        }

        // 遍历，设置订单信息
        list.forEach(bo -> {
            for (ContractOrder order : orderList) {
                if (Objects.equals(bo.getOrderId(), order.getId())) {
                    bo.setOrder(order);
                    break;
                }
            }

            // 如果没有找到对应的订单
            if (Objects.isNull(bo.getOrder())) {
                throw new MqAckException("未能获取到订单信息");
            }
        });
    }

    /**
     * 根据id查询仓位，并锁定
     *
     * @param list list
     * @since 2022/8/30 23:26
     */
    private void lockPositionWhenTrade(List<ContractTradeNewBO> list) {
        // 查询仓位信息，并锁定
        List<ContractPosition> positionList = contractPositionMapper.selectPositionWhenTrade(list);
        if (positionList.size() != list.size()) {
            throw new MqAckException("未能查询到可用仓位信息");
        }

        // 遍历仓位，检查仓位状态
        positionList.forEach(position -> ContractUtils.checkCurrentPosition(position, false));

        // 为成交记录分配各自的仓位信息
        list.forEach(bo -> {
            for (ContractPosition position : positionList) {
                if (Objects.equals(position.getId(), bo.getPositionId())) {
                    bo.setPosition(position);
                    // 判断是否是新仓位
                    bo.setIsNewPosition(Objects.equals(ContractPositionEnum.Status.NONE.getValue(), position.getStatus()));
                    break;
                }
            }

            // 如果没有找到对应的仓位
            if (Objects.isNull(bo.getPosition())) {
                throw new MqAckException("未能查询到可用仓位信息");
            }
        });
    }

    /**
     * 开仓尝试新增仓位，并预查询仓位id
     *
     * @param list list
     * @since 2022/8/30 19:33
     */
    private void initPositionWhenOpen(List<ContractTradeNewBO> list) {
        // 获取开仓成交记录
        List<ContractTradeNewBO> openList = list.stream()
                .filter(i -> {
                    i.setIsOpen(Objects.equals(ContractOrderEnum.OperationType.OPEN.getValue(), i.getOperationType()));
                    return i.getIsOpen();
                }).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(openList)) {
            return;
        }

        // 初始化仓位信息
        openList.forEach(this::initPositionWhenOpen);

        // 尝试新增仓位信息
        List<ContractPosition> positionList = openList.stream().map(ContractTradeNewBO::getPosition)
                .collect(Collectors.toList());
        contractPositionMapper.initPositionWhenOpen(positionList);

        // 预查询仓位id
        List<ContractPosition> actualPositionList = contractPositionMapper.preSelectIdWhenOpen(positionList);
        if (actualPositionList.size() != positionList.size()) {
            throw new MqAckException("开仓预创建仓位失败");
        }

        // 遍历开仓成交记录，为成交记录分配各自的仓位id
        openList.forEach(bo -> {
            // 遍历仓位
            for (ContractPosition position : actualPositionList) {
                if (Objects.equals(bo.getUserId(), position.getUserId())) {
                    bo.setPositionId(position.getId());
                    break;
                }
            }
        });
    }

    /**
     * 持久化到mysql
     *
     * @param bo bo
     * @since 2022/8/20 16:06
     */
    protected void saveToMysql(ContractTradeNewBO bo) {
        securityUtil.initCreateAbout(bo);
        if (contractTradeMapper.insert(bo) != 1) {
            throw new RuntimeException("成交记录处理失败");
        }
    }

    /**
     * 处理订单数据
     *
     * @param list list
     * @since 2022/8/20 16:06
     */
    protected void handleOrder(List<ContractTradeNewBO> list) {
        // 处理订单数据
        list.forEach(i -> {
            ContractOrder order = i.getOrder();

            BigDecimal oldNoDealNum = order.getNoDealNum();
            order.setNoDealNum(order.getNoDealNum().subtract(i.getNum()));
            // 检测成交数量是否异常
            if (BigDecimal.ZERO.compareTo(order.getNoDealNum()) > 0) {
                String errorMsg = String.format("成交数量（%s）超出订单（%s）未成交数量（%s）",
                        i.getNum(),
                        order.getId(),
                        oldNoDealNum);
                throw new MqAckException(errorMsg);
            }

            order.setPositionId(i.getPositionId());
            order.setDealAmount(order.getDealAmount().add(i.getTradeValue()));
            order.setDealNum(order.getDealNum().add(i.getNum()));
            order.setNoDealAmount(order.getNoDealAmount().subtract(i.getTradeValue()));
            order.setDealAvg(contractTradeMapper.selectAvgPriceByOrderId(order.getId(), order.getOperationType()));
            order.setHandlingFee(order.getHandlingFee().add(i.getHandlingFee()));
            order.setStatus(order.getNoDealNum().compareTo(BigDecimal.ZERO) > 0 ? ContractOrderEnum.Status.PARTIAL_DEAL.getValue() : ContractOrderEnum.Status.DEAL.getValue());
            // 判断是否完全成交
            i.setIsAllDeal(Objects.equals(ContractOrderEnum.Status.DEAL.getValue(), order.getStatus()));

            // 开仓
            if (i.getIsOpen()) {
                // 减少冻结金额
                order.setFrozenAmount(order.getFrozenAmount().subtract(i.getUnfreezeAmount()));

            } else {
                // 平仓
                // 累计收益
                BigDecimal profit = Optional.ofNullable(order.getProfit())
                        .orElse(BigDecimal.ZERO);
                order.setProfit(profit.add(i.getProfit()));
            }

            // 还需要在处理完仓位数据后，根据仓位数据计算订单收益率
        });
    }

    /**
     * 处理仓位数据
     *
     * @param list list
     * @since 2022/8/19 18:37
     */
    protected void handlePosition(List<ContractTradeNewBO> list) {
        // 处理仓位数据
        list.forEach(i -> {
            ContractPosition position = i.getPosition();

            position.setUpdateTime(i.getUpdateTime());
            position.setUpdateBy(i.getUpdateBy());

            // 开仓
            if (i.getIsOpen()) {
                position.setOpenNum(position.getOpenNum().add(i.getNum()));
                position.setNowNum(position.getNowNum().add(i.getNum()));
                // 杠杆倍数以更高的为准
                position.setMultiple(i.getMultiple().compareTo(position.getMultiple()) > 0 ? i.getMultiple() : position.getMultiple());
                position.setMaxNum(position.getNowNum().compareTo(position.getMaxNum()) > 0 ? position.getNowNum() : position.getMaxNum());
                position.setAvailableNum(position.getAvailableNum().add(i.getNum()));
                position.setOpenAvgPrice(contractTradeMapper.selectAvgPriceByPositionId(position.getId(), i.getOperationType()));
                position.setMargin(position.getMargin().add(i.getMargin()));
                position.setHandlingFee(position.getHandlingFee().add(i.getHandlingFee()));
                // 新仓位初始化开仓时间、状态
                if (i.getIsNewPosition()) {
                    position.setCloseTime(LocalDateTime.now());
                    position.setStatus(ContractPositionEnum.Status.OPEN.getValue());
                }

            } else {
                // 平仓
                BigDecimal oldNowNum = position.getNowNum();
                position.setNowNum(position.getNowNum().subtract(i.getNum()));
                // 检测成交数量是否异常
                if (BigDecimal.ZERO.compareTo(position.getNowNum()) > 0) {
                    String errorMsg = String.format("成交数量（%s）超出仓位（%s）持仓数量（%s）",
                            i.getNum(),
                            position.getId(),
                            oldNowNum);
                    throw new MqAckException(errorMsg);
                }

                position.setCloseNum(position.getCloseNum().add(i.getNum()));
                position.setClosingNum(position.getClosingNum().subtract(i.getNum()));
                position.setCloseAvgPrice(contractTradeMapper.selectAvgPriceByPositionId(position.getId(), i.getOperationType()));
                position.setMargin(position.getMargin().subtract(i.getMargin()));
                BigDecimal profit = Optional.ofNullable(position.getProfit())
                        .orElse(BigDecimal.ZERO);
                position.setProfit(profit.add(i.getProfit()));
                position.setProfitRate(OrderCalculator.getProfitRate(position.getOpenAvgPrice(),
                        position.getCloseAvgPrice(),
                        position.getMultiple(),
                        position.getSide()));
                position.setHandlingFee(position.getHandlingFee().add(i.getHandlingFee()));
                position.setStatus(position.getNowNum().compareTo(BigDecimal.ZERO) > 0 ? ContractPositionEnum.Status.PARTIAL_CLOSE.getValue() : ContractPositionEnum.Status.CLOSE.getValue());
                // 判断是否全平
                i.setIsAllClose(Objects.equals(ContractPositionEnum.Status.CLOSE.getValue(), position.getStatus()));
                position.setCloseTime(i.getIsAllClose() ? LocalDateTime.now() : null);
            }

            // 逐仓
            if (Objects.equals(ContractPositionEnum.Mode.ISOLATED.getValue(), i.getPositionMode())) {
                // 逐仓在此处计算强平价，全仓在修改完用户合约账户后发送mq计算
                ContractSymbolMongo symbol = i.getSymbol();
                PositionCalculator.calculateLiquidateIsolated(position, symbol);
            }

            // 计算订单收益率
            ContractOrder order = i.getOrder();
            order.setProfitRate(OrderCalculator.getProfitRate(position.getOpenAvgPrice(),
                    order.getDealAvg(),
                    position.getMultiple(),
                    position.getSide()));

            // 逐条处理以保证全部成功
            if (contractPositionMapper.updateById(position) != 1) {
                throw new MqAckException("修改仓位信息失败");
            }
            // 逐条处理以保证全部成功
            if (contractOrderMapper.updateById(order) != 1) {
                throw new MqAckException("修改订单信息失败");
            }
        });
    }

    /**
     * 开仓时初始化仓位数据，方便后续执行确保仓位存在的语句
     * <p>使用 insert...select from...where not exists 新增仓位
     *
     * @param bo bo
     * @since 2022/8/19 19:17
     */
    protected void initPositionWhenOpen(ContractTradeNewBO bo) {
        LocalDateTime now = LocalDateTime.now();

        // 根据成交记录组装仓位信息
        ContractPosition position = new ContractPosition();
        position.setUserId(bo.getUserId());
        position.setSymbolId(bo.getSymbolId());
        position.setSymbolName(bo.getSymbolName());
        position.setMode(bo.getPositionMode());
        position.setSide(bo.getSide());
        position.setMultiple(0);
        position.setIsRobot(bo.getIsRobot());
        position.setOpenNum(BigDecimal.ZERO);
        position.setCloseNum(BigDecimal.ZERO);
        position.setNowNum(BigDecimal.ZERO);
        position.setMaxNum(BigDecimal.ZERO);
        position.setAvailableNum(BigDecimal.ZERO);
        position.setClosingNum(BigDecimal.ZERO);
        position.setOpenAvgPrice(BigDecimal.ZERO);
        position.setCloseAvgPrice(null);
        position.setMargin(BigDecimal.ZERO);
        position.setProfit(null);
        position.setProfitRate(null);
        position.setLiquidatePrice(BigDecimalConstants.NEGATIVE_ONE);
        position.setNeedLiquidate(CommonDictEnum.YesOrNo.NO.getValue());
        position.setHandlingFee(BigDecimal.ZERO);
        position.setOpenTime(now);
        position.setCloseTime(null);
        position.setMarginCheckStatus(ContractPositionEnum.MarginCheckStatus.NORMAL.getValue());
        position.setStatus(ContractPositionEnum.Status.NONE.getValue());
        securityUtil.initCreateAbout(position);

        bo.setPosition(position);
    }

    /**
     * 计算费用
     *
     * @param trade 成交记录
     * @since 2022/8/19 16:25
     */
    protected void calculateFee(ContractTradeNewBO trade) {
    }

    /**
     * 初始化参数
     *
     * @param trade 成交记录
     * @since 2022/8/19 16:45
     */
    protected void initParam(ContractTradeNewBO trade) {
        // 判断是否做多
        trade.setIsLong(Objects.equals(ContractOrderEnum.Side.MORE.getValue(), trade.getSide()));
        // 查询币对信息
        trade.setSymbol(contractUtils.getSymbolById(trade.getSymbolId()));

        // 获得手续费率
        trade.setHandlingFeeRate(ContractUtils.getHandlingFeeRate(trade.getOrderType(), trade.getSymbol()));
    }

    /**
     * 处理成功
     *
     * @param bo bo
     * @since 2022/8/17 11:31
     */
    @Override
    public void success(ContractTradeHandleBO bo) {
        List<ContractTradeNewBO> list = bo.getNewBoList();

        List<WalletContractAccountChangeDTO> changeAssetParam = new ArrayList<>();
        list.forEach(i -> {
            // 发送k线临时成交记录&币对最新成交记录消息
            contractUtils.sendContractInitTradeMq(i);
            // 完全成交
            if (i.getIsAllDeal()) {
                afterAllDeal(i);
            }

            // 处理仓位
//			ContractPosition position = i.getNewPosition();
//			// 平仓
//			if (Objects.equals(ContractOrderEnum.OperationType.CLOSE.getValue(), i.getOperationType())) {
//				// 判断是否全部平仓、已强平
//				i.setIsAllClose(Objects.equals(ContractPositionEnum.Status.CLOSE.getValue(), position.getStatus())
//						|| Objects.equals(ContractPositionEnum.Status.LIQUIDATE.getValue(), position.getStatus()));
//
//				if (i.getIsAllClose()) {
//					afterAllClose(i);
//				}
//			}

            // 归集账户修改参数
            changeAssetParam.addAll(i.getChangeAmountParamList());
        });

        // 开启全局事务
        SeataUtils.startGlobalTx();
        // 调用钱包模块，修改合约账户
        if (!CollectionUtils.isEmpty(changeAssetParam)) {
            R r = contractUtils.changeAsset(changeAssetParam);
            if (!r.getIsSuccess()) {
                throw new MqAckException("合约账户额度不足");
            }
        }

        // throw new RuntimeException("rollback");

        // 处理相关的缓存数据，需要在上一个循环都执行成功后，才开始处理缓存
        for (ContractTradeNewBO i : list) {
            // 平仓，减少总持仓数量
            if (Objects.equals(ContractOrderEnum.OperationType.CLOSE.getValue(), i.getOperationType())) {
                contractUtils.incrPositionTotalNum(i.getUserId(),
                        i.getSymbol().getName(),
                        i.getNum().negate());
            }

            // 删除相关缓存数据
            removeMongo(i);
        }

        // 提交钱包模块分布式事务
        SeataUtils.commitGlobalTx();
        // 提交本地事务
        mysqlUtil.commitLocalTx();

        list.forEach(i -> {
            // 全仓
            if (Objects.equals(ContractPositionEnum.Mode.FULL.getValue(), i.getPositionMode())) {
                // 发送全仓自动追加保证金消息
                ContractPositionMarginAppendDTO dto = new ContractPositionMarginAppendDTO();
                dto.setInitialMargin(i.getMargin());
                dto.setPositionId(i.getPositionId());
                dto.setOperationType(i.getOperationType());
                contractUtils.sendPositionMarginAppendMq(dto);
            }

            // 如果完全成交，发送迁移历史订单消息
            if (i.getIsAllDeal()) {
                contractUtils.sendOrderMigrate(Collections.singletonList(i.getOrderId()));
            }

            // 如果完全平仓，发送迁移历史仓位消息
            if (i.getIsAllClose()) {
                contractUtils.sendPositionMigrate(Collections.singletonList(i.getPositionId()));
            }
        });
    }

    /**
     * 删除mongo中的成交记录
     *
     * @param bo bo
     * @since 2022/8/20 18:42
     */
    protected void removeMongo(ContractTradeNewBO bo) {
        // 完全成交，删除order、挂单数量缓存
        if (bo.getIsAllDeal()) {
            removeOrder(bo);
        }

        // 删除trade缓存
        removeTrade(bo.getId());
    }

    /**
     * 删除mongo中的order、挂单数量
     *
     * @param bo bo
     * @since 2022/8/20 18:42
     */
    protected void removeOrder(ContractTradeNewBO bo) {
        // 删除order缓存
        Criteria removeTaker = Criteria.where("id").is(bo.getOrderId());
        DeleteResult deleteResult = mongo.remove(Query.query(removeTaker),
                ContractOrderMongo.class,
                ContractUtils.getOrderBookName(bo.getIsBuy()));
        log.info("删除订单：" + deleteResult);

        // 限价单、条件市价单
        if (ContractUtils.isNeedChangePending(bo.getOrderType(), bo.getIsConditionPending())) {
            // 普通挂单
            if (!bo.getIsConditionPending()) {
                contractUtils.incrOrderPending(bo.getUserId(), -1);

            } else {
                // 条件单挂单。不需要减少普通挂单数量，因为新增了一条止盈止损订单
                contractUtils.incrOrderConditionPending(bo.getUserId(), -1);
            }
        }
    }

    /**
     * 删除mongo中的成交记录
     *
     * @param id 成交记录id
     * @since 2022/8/20 18:42
     */
    protected void removeTrade(Long id) {
        // 删除trade缓存
        Criteria removeTaker = Criteria.where("id").is(id);
        DeleteResult deleteResult = mongo.remove(Query.query(removeTaker),
                ContractTradeMongo.class,
                MongoTableNameConstants.CONTRACT_TRADE_NEW);
        log.info("删除交易记录：" + deleteResult);
    }

    /**
     * 完全平仓处理
     *
     * @param bo bo
     * @since 2022/8/20 17:22
     */
    @Deprecated
    protected void afterAllClose(ContractTradeNewBO bo) {
        ContractPosition position = bo.getPosition();

        // 清算剩余仓位保证金
        if (position.getMargin().compareTo(BigDecimal.ZERO) > 0) {
            log.info("清算仓位保证金：" + position.getMargin());
            // 组装清算剩余保证金参数
            WalletContractAccountChangeDTO restAmountParam = ContractUtils.getChangeAssetParamByTrade(bo,
                    position.getMargin(),
                    null,
                    WalletContractAccountDetailEnum.BusinessType.POSITION_MARGIN,
                    WalletContractAccountDetailEnum.OperationType.MINUS,
                    false);
            bo.getChangeAmountParamList().add(restAmountParam);

            // 清算剩余仓位保证金
            if (contractPositionMapper.liquidateRestMargin(position.getId(), position.getMargin()) != 1) {
                throw new RuntimeException("清算剩余仓位保证金失败");
            }
        }
    }

    /**
     * 完全成交处理
     *
     * @param bo bo
     * @since 2022/8/20 17:22
     */
    protected void afterAllDeal(ContractTradeNewBO bo) {
    }

    /**
     * 处理失败
     *
     * @param bo bo
     * @param e  异常
     * @since 2022/8/17 11:31
     */
    @Override
    public void fail(ContractTradeHandleBO bo, Exception e) {
        List<ContractTradeNewBO> list = bo.getNewBoList();

        // 如果没有集合，查询成交记录时就已经报错，尝试根据taker回滚
        if (CollectionUtils.isEmpty(list)) {
            ContractTradeMongo trade = bo.getMongo();

            if (!Objects.isNull(trade)) {
                // 回滚订单薄交易数据
                rollbackOrderBook(trade);
                try {
                    ContractTradeMongo makerTrade = getTrade(trade.getMakerTradeId());
                    rollbackOrderBook(makerTrade);
                } catch (Exception ignored) {
                }

                // 删除mongo中的两条交易记录的缓存
                removeTrade(trade.getId());
                removeTrade(trade.getMakerTradeId());
            }

        } else {
            // 查询到成交记录，在后续处理中报错
            list.forEach(i -> {
                // 回滚订单薄交易数据
                rollbackOrderBook(i);
                // 删除mongo中的交易记录的缓存
                removeTrade(i.getId());
                // 如果增加过止盈止损挂单数量
                if (i.getHasAddStopOrder()) {
                    contractUtils.incrOrderPending(i.getUserId(), -1);
                }
            });
        }
    }

    /**
     * 回滚订单薄数据
     *
     * @param trade 成交记录
     * @since 2022/8/29 21:42
     */
    protected void rollbackOrderBook(ContractTradeMongo trade) {
        Criteria criteria = Criteria.where("id").is(trade.getOrderId());
        Query query = Query.query(criteria);

        Update update = new Update();
        update.inc("dealAmount", trade.getTradeValue().negate())
                .inc("dealNum", trade.getNum().negate())
                .inc("noDealAmount", trade.getTradeValue())
                .inc("noDealNum", trade.getNum());

        // 尝试回滚contract_order_book
        String orderBookName = ContractUtils.getOrderBookName(trade.getIsBuy());
        UpdateResult updateResult = mongo.updateFirst(query, update, ContractOrderMongo.class, orderBookName);
        log.info("尝试回滚contract_order_book：" + updateResult);
    }
}
